
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>基于K8S构建企业级Jenkins CICD平台实战（三） 之 带你实战Spring bootCloud 项目 CICD jenkins自动化构建、部署过程</title>
            </head>
            <body>
            <a href="https://andyoung.blog.csdn.net">原作者博客</a>
            <div id="content_views" class="markdown_views prism-atom-one-light">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <h3><a id="_1"></a>需要环境</h3> 
<ul><li> <p><a href="https://blog.csdn.net/agonie201218/article/details/120859712">Git（GitLab）</a></p> </li><li> <p><a href="https://blog.csdn.net/agonie201218/article/details/115691894">Harbor 私服</a></p> </li><li> <p><a href="https://andyoung.blog.csdn.net/article/details/121851209" rel="nofollow">kubernetes-plugin 使用</a></p> </li><li> <p><a href="https://blog.csdn.net/agonie201218/category_11326432.html">Kubernetes</a></p> </li><li> <p><a href="https://andyoung.blog.csdn.net/article/details/121850743" rel="nofollow">jenkins</a></p> </li></ul> 
<p>通过前面三篇我们已经了解了jenkins和 kubernetes-plugin的使用。还没过的小伙伴可以看下</p> 
<ul><li><a href="https://andyoung.blog.csdn.net/article/details/121850743" rel="nofollow">基于K8S构建企业级Jenkins CI/CD平台实战（一） 之 环境搭建</a></li><li><a href="https://andyoung.blog.csdn.net/article/details/121851209" rel="nofollow">基于K8S构建企业级Jenkins CI/CD平台实战（二） 之 kubernetes-plugin 插件使用</a></li><li><a href="https://andyoung.blog.csdn.net/article/details/121860878" rel="nofollow">Jenkins Pipeline构建流水线发布</a></li></ul> 
<p>接下来为大家带来Java 项目的基于K8S构建企业级Jenkins CI/CD过程</p> 
<h3><a id="PipelineCI_23"></a>编写Pipeline脚本完成CI阶段</h3> 
<p>我们使用<a href="https://andyoung.blog.csdn.net/article/details/121851209" rel="nofollow">第二节</a>中配置好的 <code>podTemplate</code> 模板<code>jnlp-maven</code>。</p> 
<p>再<code>jnlp</code>容器中执行：</p> 
<ul><li> 
  <ol><li>使用 git 拉取代码。使用码云上的springboot demo ：<code>https://gitee.com/andanyoung/springboot-hello</code>，public 仓库，git_auth 就不需要了</li><li>使用maven 命令打包成 jar</li><li>使用docker 命令打包成镜像，并推送至私服</li></ol> </li></ul> 
<pre><code>//项目git 地址
def git_address = "https://gitee.com/andanyoung/springboot-hello.git"
//项目git 分支地址
def git_branch = "master"
//git 证书ID
def git_auth = ""  

//打包镜像名称
def docker_image_name = "andanyoung/springboot-hello:v1.0"
//docker registry 地址
def docker_registry = "" 
def docker_registry_auth = "b0f6a24a-f587-4b91-bc1f-9de8b07c762f"   

def docker_host = "tcp://192.168.0.192:2375"

podTemplate (inheritFrom: "jnlp-maven"){
  node(POD_LABEL){
      // 第一步
      stage('拉取代码'){
         git branch: "${git_branch}", credentialsId: "${git_auth}", url: "${git_address}"
      }
      // 第二步
      stage('代码编译'){
          sh "mvn clean package -Dmaven.test.skip=true"
      }
      // 第三步
      stage('构建镜像'){
          //Harbor镜像仓库登录验证，
          withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
            sh """
              echo '
                FROM openjdk:8-jdk-alpine
                ADD target/app.jar app.jar
                ENTRYPOINT [ "sh", "-c", "java -jar app.jar" ]
              ' &gt; Dockerfile
              export DOCKER_HOST="${docker_host}"
              docker build -t ${docker_image_name} .
              docker login -u ${username} -p '${password}' ${docker_registry}
              docker push ${docker_image_name}
            """
            }
      }
  }
}

</code></pre> 
<blockquote> 
 <p>pipeline script变量docker_registry_auth、git_auth、k8s_auth通过保存在jenkins凭据中相应的凭据ID。</p> 
</blockquote> 
<p>各个阶段注释都是很清楚的。需要说明一点的是 <code>export DOCKER_HOST="${docker_host}"</code> 配置docker 守护进程（Daemon socket）host</p> 
<p>如果你不写这个默认使用<code>unix:///var/run/docker.sock</code> 连接，就会报<code>connect: permission denied</code>权限不足错误，因为宿主机docker使用root起的，而docker运行jenkins是使用 jenkins（id 1000） 用户的。</p> 
<pre><code>Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/auth": dial unix /var/run/docker.sock: connect: permission denied
</code></pre> 
<p>解决方案一：是在宿主机上执行<code>chmod 777 /var/run/docker.sock</code> 。但这个太low，宿主机一重启权限有不对了。有需要重新执行。所以还是使用方案二吧</p> 
<p>解决方案二：<code>docker -H tcp://0.0.0.0:2375 ps</code> 或者<code>export DOCKER_HOST="tcp://0.0.0.0:2375" &amp;&amp; docker ps</code>。指定dockerd host。前提是dockerd启动需要开启tcp 连接：</p> 
<pre><code>sudo dockerd -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
</code></pre> 
<p>或者修改<code>vi /usr/lib/systemd/system/docker.service</code>：</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/63aa89c29f3465066d15a762543c6334.png" alt="image-20211214155338188"></p> 
<p>参考：<a href="https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file" rel="nofollow">https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file</a></p> 
<h4><a id="docker_docker_registry_auth_105"></a>配置docker私服 <code>docker_registry_auth</code>账号密码</h4> 
<blockquote> 
 <p>PS: git_auth 同理</p> 
</blockquote> 
<p>我这边为了演示直接使用的docker官方仓库，生产上当然是需要使用docker私服。</p> 
<p>使用docker官方仓库（类似于github） <a href="https://hub.docker.com/" rel="nofollow">https://hub.docker.com/</a>,先去注册账号密码。</p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/8569e63904a42638347918619d339bf3.png" alt="image-20211214140233601"></p> 
<ul><li> <p>点击保存生成凭据 复制配置id到我们的pipeline</p> <p><img src="https://i-blog.csdnimg.cn/blog_migrate/f0e3d1d77b2285574cb049730adc8452.png" alt="image-20211214140416720"></p> <h4><a id="_121"></a>点击执行，构建成功</h4> <p><img src="https://i-blog.csdnimg.cn/blog_migrate/1e896b2779c886b382a43e40b9cbd8b4.png" alt="image-20211214155848664"></p> </li></ul> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/2bf0fa2bc27b222ea6269eb22685282b.png" alt="image-20211214155516442"></p> 
<h4><a id="dockerpush_129"></a>查看docker私仓，push成功</h4> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/2becdb85e91f284e8110d6ac8cc74e47.png" alt="image-20211214155930934"></p> 
<h4><a id="k8s__133"></a>运行再k8s 中部署</h4> 
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-k8s
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-k8s
  template:
    metadata:
      labels:
        app: hello-k8s
    spec:
      affinity:
        # 反亲和性调度 使各个pod不在同个node里
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                topologyKey: kubernetes.io/hostname
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - hello-k8s
      containers:
        - name: pod-hello-k8s
          image: andanyoung/springboot-hello:v1.0
          imagePullPolicy: Always
          resources:
            limits:
              memory: 2G
            requests:
              memory: 2G

</code></pre> 
<p>如果是私服记得加上<code>imagePullSecrets</code> ,<code>image</code> 改为 私服地址<code>host:ip/andanyoung/springboot-hello:v1.0</code></p> 
<h3><a id="JenkinsK8s_CD__177"></a>Jenkins在K8s中持续部署 CD 阶段</h3> 
<h4><a id="Cluster_Role__179"></a>新增Cluster Role 集群权限</h4> 
<p>在<a href="https://andyoung.blog.csdn.net/article/details/121851209" rel="nofollow">第二节</a>中已经添加了用户<code>jenkins</code>,在这个里我们需要给他就是Deployment的一些部署的（Cluster Role）权限。</p> 
<pre><code>kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cluster-jenkins-dev-ops
rules:
  - verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
    apiGroups:
      - ''
    resources:
      - pods
  - verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
    apiGroups:
      - ''
    resources:
      - pods/exec
  - verbs:
      - get
      - list
      - watch
    apiGroups:
      - ''
    resources:
      - pods/log
  - verbs:
      - watch
    apiGroups:
      - ''
    resources:
      - events
  - verbs:
      - get
    apiGroups:
      - ''
    resources:
      - secrets
  - verbs:
      - create
      - delete
      - deletecollection
      - patch
      - update
    apiGroups:
      - apps
    resources:
      - daemonsets
      - deployments
      - deployments/rollback
      - deployments/scale
      - replicasets
      - replicasets/scale
      - statefulsets
      - statefulsets/scale
  - verbs:
      - get
      - list
      - watch
    apiGroups:
      - apps
    resources:
      - controllerrevisions
      - daemonsets
      - daemonsets/status
      - deployments
      - deployments/scale
      - deployments/status
      - replicasets
      - replicasets/scale
      - replicasets/status
      - statefulsets
      - statefulsets/scale
      - statefulsets/status

</code></pre> 
<h4><a id="Cluster_Role_Binding__272"></a>Cluster Role Binding 绑定权限</h4> 
<pre><code>kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cluster-jenkins-dev-ops-binding
subjects:
  - kind: User
    apiGroup: rbac.authorization.k8s.io
    name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-jenkins-dev-ops

</code></pre> 
<h4><a id="CD__290"></a>CD 阶段</h4> 
<p>部署无非是执行重启，或者是更新版本，先来看看命令吧。</p> 
<ul><li> <p>重启</p> <pre><code>kubectl rollout restart deployment hello-k8s
</code></pre> </li><li> <p>更新版本</p> <pre><code>kubectl set image deployments/hello-k8s pod-hello-k8s=andanyoung/springboot-hello:V2.0
</code></pre> 
  <blockquote> 
   <pre><code>其中deployments/hello-k8s    表示部署名称，有上面的命令得到
pod-hello-k8s=andanyoung/springboot-hello:V2.0    表示pod部署更新到哪个容器
</code></pre> 
  </blockquote> </li><li> <p>其他命令</p> <p>还可以执行其他kuctl命令,但要注意<code>namespace</code></p> </li></ul> 
<h4><a id="pipline_315"></a>重启命令pipline：</h4> 
<pre><code>//项目git 地址
def git_address = "https://gitee.com/andanyoung/springboot-hello.git"
//项目git 分支地址
def git_branch = "master"
//git 证书ID
def git_auth = ""  

//打包镜像名称
def docker_image_name = "andanyoung/springboot-hello:v1.0"
//docker registry 地址
def docker_registry = "" 
def docker_registry_auth = "b0f6a24a-f587-4b91-bc1f-9de8b07c762f"   

def docker_host = "tcp://192.168.0.192:2375"

podTemplate (inheritFrom: "jnlp-maven"){
  node(POD_LABEL){
      // 第一步
      stage('拉取代码'){
         git branch: "${git_branch}", credentialsId: "${git_auth}", url: "${git_address}"
      }
      // 第二步
      stage('代码编译'){
          sh "mvn clean package -Dmaven.test.skip=true"
      }
      // 第三步
      stage('构建镜像'){
          //Harbor镜像仓库登录验证，
          withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
            sh """
              echo '
                FROM openjdk:8-jdk-alpine
                ADD target/app.jar app.jar
                ENTRYPOINT [ "sh", "-c", "java -jar app.jar" ]
              ' &gt; Dockerfile
              export DOCKER_HOST="${docker_host}"
              docker build -t ${docker_image_name} .
              docker login -u ${username} -p '${password}' ${docker_registry}
              docker push ${docker_image_name}
            """
            }
      }
      // 第四步
      container('jnlp-kubectl'){
          stage('部署到K8S平台'){
            sh 'kubectl config view'
            sh 'kubectl rollout restart deployment hello-k8s'
          }
        }
  }
}

</code></pre> 
<h5><a id="_374"></a>运行完成，查看重启结果</h5> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/5ca8ad72488200ccfaf99340c6ba0d68.png" alt="image-20211214162845803"></p> 
<h4><a id="pipline_378"></a>更新版本命令pipline：</h4> 
<pre><code>//项目git 地址
def git_address = "https://gitee.com/andanyoung/springboot-hello.git"
//项目git 分支地址
def git_branch = "master"
//git 证书ID
def git_auth = ""  

//打包镜像名称
def docker_image_name = "andanyoung/springboot-hello"
//打包镜像tag
def docker_image_tag = "v2.0"
//docker registry 地址
def docker_registry = "" 
def docker_registry_auth = "b0f6a24a-f587-4b91-bc1f-9de8b07c762f"   

def docker_host = "tcp://192.168.0.192:2375"

podTemplate (inheritFrom: "jnlp-maven"){
  node(POD_LABEL){
      // 第一步
      stage('拉取代码'){
         git branch: "${git_branch}", credentialsId: "${git_auth}", url: "${git_address}"
      }
      // 第二步
      stage('代码编译'){
          sh "mvn clean package -Dmaven.test.skip=true"
      }
      // 第三步
      stage('构建镜像'){
          //Harbor镜像仓库登录验证，
          withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
            sh """
              echo '
                FROM openjdk:8-jdk-alpine
                ADD target/app.jar app.jar
                ENTRYPOINT [ "sh", "-c", "java -jar app.jar" ]
              ' &gt; Dockerfile
              export DOCKER_HOST="${docker_host}"
              docker build -t ${docker_image_name}:${docker_image_tag} .
              docker login -u ${username} -p '${password}' ${docker_registry}
              docker push ${docker_image_name}:${docker_image_tag}
            """
            }
      }
      // 第四步
      container('jnlp-kubectl'){
          stage('部署到K8S平台'){
            sh 'kubectl config view'
            sh "kubectl set image deployments/hello-k8s pod-hello-k8s=andanyoung/springboot-hello:${docker_image_tag}"
          }
        }
  }
}
</code></pre> 
<h5><a id="_436"></a>运行完成，查看重启结果</h5> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/c39ba7d1d2803e5be815dc3aca880dea.png" alt="image-20211214164214940"></p> 
<p><img src="https://i-blog.csdnimg.cn/blog_migrate/ce187d99236db0539dd092b98f616e70.png" alt="image-20211214163845650"></p> 
<h3><a id="k8sSpring_cloud_442"></a>k8s部署Spring cloud</h3> 
<p>上面展示了K8s如何部署 Spring boot 项目，Spring Cloud 其实可以看出基于Spring boot 的按业务功能划分为多个 Spring boot 项目而已。所有部署Spring Cloud就是部署多个Spring boot 项目。在这里就不多说了</p> 
<h4><a id="_446"></a>更复杂的可以使用参数化构建</h4>
                </div>
            </body>
            </html>
            